#include "travel.h"
#include "semantic.h"
#include "translate.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void Typee(Node* type, SNode* stype) {
    lexAssign(stype, type, BASICTYPE);
    // printf("%s\n", type->strval);
}
void ID(Node *id, SNode *sid) {
    lexAssign(sid, id, NAMEVAL);
    // printf("%s\n", id->strval);
}
void INT(Node* ival, SNode* sival) {
    lexAssign(sival, ival, CONSTANTINT);
    // printf("%d\n", ival->intval);
}
void FLOAT(Node* fval, SNode* sfval) {
    lexAssign(sfval, fval, CONSTANTFLOAT);
    // printf("%f\n", fval->floatval);
}
  
void Program(Node* program) {
    // printf("Program\n");
    ExtdefList(program->ch[0]);

    semanticProgram(program);

    // showCodes();
}

void ExtdefList(Node* extdefList) {
    if (extdefList == NULL) return ;
    Extdef(extdefList->ch[0]);
    ExtdefList(extdefList->ch[1]);

    semanticExtdefList(extdefList);
}

void Extdef(Node* extDef) {
    // printf("Extdef\n");
    Specifier(extDef->ch[0]); // get specifier
    switch (extDef->ch[1]->elem) {
        case SybExtDecList : 
            ExtDecList(extDef->ch[1]); 
        case SybSEMI : 
            break;
        case SybFunDec : 
            FunDec(extDef->ch[1]); 
            CompSt(extDef->ch[2]);
            break;
        default : 
            break;
    }

    semanticExtdef(extDef);
}

void ExtDecList(Node* extdecList) {
    // printf("ExtDecList\n");
    VarDec(extdecList->ch[0]);
    if (extdecList->cnt > 1) {
        ExtDecList(extdecList->ch[2]);
        extdecList->snode->nexDec = extdecList->ch[2]->snode;
        // sextdecList->nexDec = sextdecList->ch[2];
    }

    semanticExtDecList(extdecList);
}

void Specifier(Node* specifier) {
    // printf("Specifier\n");
    switch (specifier->ch[0]->elem) {
        case SybTYPE : 
            Typee(specifier->ch[0], specifier->ch[0]->snode); 
            // Typee(specifier->ch[0], sspecifier->ch[0]); 
            break;
        case SybStructSpecifier : 
            StructSpecifier(specifier->ch[0]); 
            break;
        default : 
            break;
    }

    semanticSpecifier(specifier);
}

void StructSpecifier(Node* structSpecifier) {
    // printf("StructSpecifier\n");
    switch (structSpecifier->ch[1]->elem) {
        case SybOptTag :
            OptTag(structSpecifier->ch[1]);
            DefList(structSpecifier->ch[3]);
            break;
        case SybTag :
            Tag(structSpecifier->ch[1]);
            break;
        default : 
            break;
    }

    semanticStructSpecifier(structSpecifier);
}

void OptTag(Node* optTag) {
    if (optTag == NULL) return ;
    ID(optTag->ch[0], optTag->ch[0]->snode);
    // ID(optTag->ch[0], soptTag->ch[0]);

    semanticOptTag(optTag);
}

void Tag(Node *tag) {
    ID(tag->ch[0], tag->ch[0]->snode);
    // ID(tag->ch[0], stag->ch[0]);
    
    semanticTag(tag);
}

void VarDec(Node *varDec) {
    // printf("VarDec\n");
    switch (varDec->cnt) {
        case 1 :
            ID(varDec->ch[0], varDec->ch[0]->snode);
            // ID(varDec->ch[0], svarDec->ch[0]);
            break;
        case 4 :
            VarDec(varDec->ch[0]);
            INT(varDec->ch[2], varDec->ch[2]->snode);
            // INT(varDec->ch[2], svarDec->ch[2]);
            break;
        default : 
            break;;
    }

    semanticVarDec(varDec);
}

void FunDec(Node *funDec) {
    // printf("FunDec\n");
    ID(funDec->ch[0], funDec->ch[0]->snode);
    // ID(funDec->ch[0], sfunDec->ch[0]);
    if (funDec->cnt > 3) {
        VarList(funDec->ch[2]);
    }

    semanticFunDec(funDec);
}

void VarList(Node* varList) {
    // printf("VarList\n");
    ParamDec(varList->ch[0]);
    if (varList->cnt > 1) {
        VarList(varList->ch[2]);
    }

    semanticVarList(varList);
}

void ParamDec(Node* paramDec) {
    Specifier(paramDec->ch[0]);
    VarDec(paramDec->ch[1]);

    semanticParamDec(paramDec);
}

void CompSt(Node* compSt) {
    DefList(compSt->ch[1]);
    StmtList(compSt->ch[2]);

    semanticCompSt(compSt);
}

void StmtList(Node* stmtList) {
    if (stmtList == NULL) return ;
    Stmt(stmtList->ch[0]);
    StmtList(stmtList->ch[1]);

    semanticStmt(stmtList);
}

void Stmt(Node* stmt) {
    switch (stmt->cnt) {
        case 2 :
            Exp(stmt->ch[0]);
            break;
        case 1 :
            CompSt(stmt->ch[0]);
            break;
        case 3 :
            Exp(stmt->ch[1]);
            break;
        case 5 :
            Exp(stmt->ch[2]);
            Stmt(stmt->ch[4]);
            break;
        case 7 :
            Exp(stmt->ch[2]);
            Stmt(stmt->ch[4]);
            Stmt(stmt->ch[6]);
            break;
        default :
            break;
    }

    semanticStmt(stmt);
}

void DefList(Node* deflist) {
    if (deflist == NULL) return ;
    Def(deflist->ch[0]);
    DefList(deflist->ch[1]);

    semanticDefList(deflist);
}

void Def(Node* def) {
    Specifier(def->ch[0]);
    DecList(def->ch[1]);

    semanticDef(def);
}

void DecList(Node* decList) {
    // printf("DecList\n");
    Dec(decList->ch[0]);
    if (decList->cnt > 1) {
        DecList(decList->ch[2]);
    }

    semanticDecList(decList);
}

void Dec(Node* dec) {
    // printf("Dec\n");
    VarDec(dec->ch[0]);
    if (dec->cnt > 1) {
        Exp(dec->ch[2]);
    }

    semanticDec(dec);
}

void Exp(Node* exp) {
    switch (exp->cnt) {
        case 3 :
            if (exp->ch[2]->elem == SybExp) {
                Exp(exp->ch[0]);
                Exp(exp->ch[2]);
            } else if (exp->ch[1]->elem == SybExp) { // (Exp)
                Exp(exp->ch[1]);
            } else if (exp->ch[0]->elem == SybID) {
                ID(exp->ch[0],exp->ch[0]->snode);
                // ID(exp->ch[0], sexp->ch[0]);
            } else if (exp->ch[1]->elem == SybDOT) {
                Exp(exp->ch[0]);
                ID(exp->ch[2], exp->ch[2]->snode);
                // ID(exp->ch[2], sexp->ch[2]);
            }
            break;
        case 2 :
            Exp(exp->ch[1]);
            break;
        case 4 :
            if (exp->ch[2]->elem == SybArgs) {
                ID(exp->ch[0], exp->ch[0]->snode);
                // ID(exp->ch[0], sexp->ch[0]);
                Args(exp->ch[2]);
            } else if(exp->ch[2]->elem == SybExp) {
                Exp(exp->ch[0]);
                Exp(exp->ch[2]);
            }
            break;
        case 1 :
            if (exp->ch[0]->elem == SybID) {
                ID(exp->ch[0], exp->ch[0]->snode);
                // ID(exp->ch[0], sexp->ch[0]);
            } else if (exp->ch[0]->elem == SybINT) {
                INT(exp->ch[0], exp->ch[0]->snode);
                // INT(exp->ch[0], sexp->ch[0]);
            } else {
                FLOAT(exp->ch[0], exp->ch[0]->snode);
                // FLOAT(exp->ch[0], sexp->ch[0]);
            }
        default :
            break;
    }

    semanticExp(exp);
    // translateExp(exp, getTemp());
    
}

void Args(Node* args) {
    Exp(args->ch[0]);
    if (args->cnt > 1) {
        Args(args->ch[2]);
    }
    
    semanticArgs(args);
}

void addReadWriteSymbol() {
    insertSymbol("read");
    insertSymbol("write");
}

void fillExtSymbol(char* name) {
    // return type
    Type* type = malloc(sizeof(Type));
    type->kind = BASIC;
    type->info.basic = BASIC_INT;

    SNode* specifier = malloc(sizeof(SNode));
    specifier->type = type;

    SNode* func = malloc(sizeof(SNode));
    func->name = malloc(sizeof(char) * strlen(name));
    strcpy(func->name, name);

    if (!strcmp(name, "write")) {
        Type* atype = malloc(sizeof(Type));
        atype->kind = BASIC;
        atype->info.basic = BASIC_INT;
        SNode* argument = malloc(sizeof(SNode));
        argument->type = atype;

        func->nexPar = argument;
    }
    fillFunctions(specifier, func);   
}

void fillReadWriteSymbol() {
   fillExtSymbol("read");
   fillExtSymbol("write");  
}

void semanticAnalysis(Node* root, char* out) {

    prepareForSemanticAnalysis(root);
    addReadWriteSymbol();
    createSymbolTable();
    fillReadWriteSymbol();

    Program(root);

    // set base of temp before translate
    // setTempBase();
    translateProgram(root);

    showCodes(out); 

}
